///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*	Contains code for rays.
*	\file		LaborRay.h
*	\author		Pierre Terdiman
*	\date		April, 4, 2000
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Include Guard
#ifndef __LABORRAY_H__
#define __LABORRAY_H__

#include "LABORMatrix4x4.h"
#include "LaborCoreDef.h"
NAMESPACE_LABOR3D_BEGIN
class  LABORCORE_API CLRay
{
public:
	//! Constructor
	inline					CLRay()																{}
	//! Constructor
	inline					CLRay(const CLPoint& orig, const CLPoint& dir) : mOrig(orig), mDir(dir)	{}
	//! Copy constructor
	inline					CLRay(const CLRay& ray) : mOrig(ray.mOrig), mDir(ray.mDir)				{}
	//! Destructor
	inline					~CLRay()																{}

	/** Gets the position of a point t units along the ray. */
	CLPoint getPoint(float t) const { 
		return CLPoint(mOrig + (mDir * t));
	}

	/** Sets the origin of the ray. */
	void setOrigin(const CLPoint& origin) {mOrig = origin;} 
	/** Gets the origin of the ray. */
	const CLPoint& getOrigin(void) const {return mOrig;} 

	/** Sets the direction of the ray. */
	void setDirection(const CLPoint& dir) {mDir = dir;} 
	/** Gets the direction of the ray. */
	const CLPoint& getDirection(void) const {return mDir;} 



	bool intersects(const CLPlane& plane, CLPoint &outPoint );
	std::pair<bool, float> intersects(const CLPlane& plane ) const;




	float	squareDistance(const CLPoint& point, float* t=null)	const;
	inline			float	distance(const CLPoint& point, float* t=null)			const			{ return sqrtf(squareDistance(point, t));	}

	CLPoint	mOrig;		//!< Ray origin
	CLPoint	mDir;		//!< Normalized direction

};

inline void computeReflexionVector(CLPoint& reflected, const CLPoint& incoming_dir, const CLPoint& outward_normal)
{
	reflected = incoming_dir - outward_normal * 2.0f * (incoming_dir|outward_normal);
}

inline void computeReflexionVector(CLPoint& reflected, const CLPoint& source, const CLPoint& impact, const CLPoint& normal)
{
	CLPoint V = impact - source;
	reflected = V - normal * 2.0f * (V|normal);
}

inline void decomposeVector(CLPoint& normal_compo, CLPoint& tangent_compo, const CLPoint& outward_dir, const CLPoint& outward_normal)
{
	normal_compo = outward_normal * (outward_dir|outward_normal);
	tangent_compo = outward_dir - normal_compo;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*	Transforms a direction vector from world space to local space
*	\param		local_dir	[out] direction vector in local space
*	\param		world_dir	[in] direction vector in world space
*	\param		world		[in] world transform
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline void computeLocalDirection(CLPoint& local_dir, const CLPoint& world_dir, const CLMatrix4x4& world)
{
	// Get world direction back in local space
	//		Matrix3x3 InvWorld = world;
	//		local_dir = InvWorld * world_dir;
	local_dir = CLMatrix3x3(world) * world_dir;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*	Transforms a position vector from world space to local space
*	\param		local_pt	[out] position vector in local space
*	\param		world_pt	[in] position vector in world space
*	\param		world		[in] world transform
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline void computeLocalPoint(CLPoint& local_pt, const CLPoint& world_pt, const CLMatrix4x4& world)
{
	// Get world vertex back in local space
	CLMatrix4x4 InvWorld = world;
	InvWorld.invert();
	local_pt = world_pt * InvWorld;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
*	Transforms a ray from world space to local space
*	\param		local_ray	[out] ray in local space
*	\param		world_ray	[in] ray in world space
*	\param		world		[in] world transform
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
inline void computeLocalRay(CLRay& local_ray, const CLRay& world_ray, const CLMatrix4x4& world)
{
	// Get world ray back in local space
	computeLocalDirection(local_ray.mDir, world_ray.mDir, world);
	computeLocalPoint(local_ray.mOrig, world_ray.mOrig, world);
}

NAMESPACE_LABOR3D_END

#endif // __LABORRAY_H__
